#include <iostream>
#include <helper/iter/Iterator.h>
#include <helper/unittest/Tracer.h>


class Object {
public:
	Object(const Object & obj){
		//std::cout << "copy ctor ";
		_id = obj._id;
	}
	Object(int id = 0)
	:_id(id){
	}
	int get() const{
		return _id;
	}
	void set(int id) {
		_id = id;
	}
private:
		int _id;
};


#include <vector>
template<typename T>
class MyObjectIterator : public helper::iter::Iterator<T>{
public:
	typedef T Item;

	MyObjectIterator()
	{
		if constexpr (std::is_pointer_v<Item>){
			_obj.push_back(new Object(0));
			_obj.push_back(new Object(1));
			_obj.push_back(new Object(2));
			_obj.push_back(new Object(3));
		}
		else{
			_obj = {0, 1, 2, 3};
		}
		//std::cout <<  " --- iter created" << std::endl;
	}
	std::optional<Item> next() override{
			if(_index < _obj.size()){
				if constexpr (std::is_pointer_v<Item>){
					auto obj = _obj[_index++];
					return std::make_optional<Item>(obj);
				}
				else{
					auto & obj = _obj[_index++];
					//cp vec[index] to optional
					return std::make_optional<Item>(obj);
				}
			}
			return std::nullopt;
	}
private:
	std::vector<Item> _obj;
	size_t _index = (size_t)0;
};



#include <gtest/gtest.h>
using namespace helper::iter;

TEST(MyObjectIteratorTest, testCount){
  MyObjectIterator<Object> iter;
  ASSERT_EQ((size_t)4, iter.count());
  std::cout << std::endl;
}

TEST(MyObjectIteratorTest, testForeach){
  EXPECT.reset()
    .operation("Iterator.object.foreach, obj:0")
    .operation("Iterator.object.foreach, obj:1")
    .operation("Iterator.object.foreach, obj:2")
    .operation("Iterator.object.foreach, obj:3")
  ;
  TRACER.reset();
  MyObjectIterator<Object> iter;
  iter.foreach([](const Object & obj){
      TRACER.operation("Iterator.object.foreach, obj:" + std::to_string(obj.get()));
  });
  ASSERT_EQ(EXPECT, TRACER);
}

TEST(MyObjectIteratorTest, testPtrForeach){
  EXPECT.reset()
    .operation("Iterator.object.ptr.foreach, obj:0")
    .operation("Iterator.object.ptr.foreach, obj:1")
    .operation("Iterator.object.ptr.foreach, obj:2")
    .operation("Iterator.object.ptr.foreach, obj:3")
  ;
  TRACER.reset();
  MyObjectIterator<Object*> iter;
  iter.foreach([](const Object* obj){
      TRACER.operation("Iterator.object.ptr.foreach, obj:" + std::to_string(obj->get()));
  });
  ASSERT_EQ(EXPECT, TRACER);
}
TEST(MyObjectIteratorTest, testPtrCount){
		MyObjectIterator<Object*> iter;
		ASSERT_EQ((size_t)4, iter.count());
		std::cout << std::endl;
}

/* TODO:
class MyOIterator: public Iterator<int &> {
};
*/
//TODO: Ref ????
TEST(MyObjectIteratorItest, testRefCount){
	//MyObjectIterator<Object&> iter;
}
